package indigo.mybatis.dao.criterion;

import static java.util.Objects.requireNonNull;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import com.google.common.collect.ImmutableList;

public class Condition extends Criterion {
	private static final long serialVersionUID = 5246639676534493414L;

	private final String property;
	private final Operator operator;
	private final List<Object> operands;

	public Condition(String property, Operator operator, Object... operands) {
		this(property, operator, Arrays.asList(operands));
	}

	public Condition(String property, Operator operator, Collection<?> operands) {
		this.property = requireNonNull(property, "property cannot be null");
		this.operator = requireNonNull(operator, "operator cannot be null");
		this.operands = checkOperands(requireNonNull(operands, "operands cannot be null"), operator);
	}

	@Override
	public String toString() {
		return String.format("Condition {property=%s, operator=%s, operands=%s}",
				property, operator.name(), operands.toString());
	}

	@Override
	public List<FlattenPart> flatten() {
		return ImmutableList.of(new ConditionFlattenPart(this));
	}

	public String getProperty() {
		return property;
	}

	public Operator getOperator() {
		return operator;
	}

	public List<Object> getOperands() {
		return operands;
	}

	public static enum Operator {
		EQ(1, 1), EQ_CI(1, 1), NE(1, 1), NE_CI(1, 1),
		GT(1, 1), GE(1, 1), LT(1, 1), LE(1, 1),
		IS_NOT_NULL(0, 0), IS_NULL(0, 0),
		LIKE(1, 1), NOT_LIKE(1, 1), LIKE_CI(1, 1), NOT_LIKE_CI(1, 1),
		BETWEEN(2, 2), NOT_BETWEEN(2, 2),
		IN(1, Integer.MAX_VALUE), NOT_IN(1, Integer.MAX_VALUE);

		private final int minOperandCount;
		private final int maxOperandCount;

		private Operator(int minOperandCount, int maxOperandCount) {
			this.minOperandCount = minOperandCount;
			this.maxOperandCount = maxOperandCount;
		}

		public int getMinOperandCount() {
			return minOperandCount;
		}

		public int getMaxOperandCount() {
			return maxOperandCount;
		}
	}

	private static List<Object> checkOperands(Collection<?> operands, Operator operator) {
		int operandCount = operands.size();
		if (operandCount < operator.getMinOperandCount()) {
			throw new IllegalStateException(String.format("Too few operands for operator '%s': %d",
					operator.name(), operandCount));
		}
		if (operandCount > operator.getMaxOperandCount()) {
			throw new IllegalStateException(String.format("Too many operands for operator '%s': %d",
					operator.name(), operandCount));
		}
		return ImmutableList.copyOf(operands);
	}

	private static class ConditionFlattenPart extends FlattenPart {
		public ConditionFlattenPart(Condition condition) {
			super(Type.CONDITION, condition);
		}
	}
}
